﻿namespace Generator
{
    class ManMain : IFuncs
    {
        private IFuncs f;

        public ManMain(IFuncs funcs)
        {
            f = funcs;
        }

        public object GenerateStepFunc(object[] args)
        {
            const int worldState = 0;
            const int ghostCode = 1;

            var res = Return(
                MakePair(Const(1), MakeClosure("lambda_step", MakeStep))
                );
            Flush();
            return res;
        }

        //Аргументы, от которых вызвана Step
        private object[] _stepArgs;

        public object MakeStep(params object[] args)
        {
            object aiState = Param(args, 0);
            object worldState = Param(args, 1);
            _stepArgs = args;
            
            //return Return(
            //    MakePair(
            //        Const(1),
            //        Select(
            //            IfNotEqual( 
            //                Wall, 
            //                GetCell(GetManX,Dec(GetManY))
            //            ),
            //            MoveUp,
            //            Select(
            //                IfNotEqual(
            //                    Wall,
            //                    GetCell(Inc(GetManX), GetManY)
            //                ),
            //                MoveRight,
            //                Select(
            //                    IfNotEqual(
            //                        Wall,
            //                        GetCell(GetManX, Inc(GetManY))
            //                    ),
            //                    MoveDown,
            //                    MoveLeft
            //                )
            //            )
            //        )
            //    )
            //);

            //return Return(
            //    MakePair(
            //        Const(1),
            //        Select(
            //            IfEqual(GetMaxNeigbour(), GetCellRange(Dec(GetManY), GetManX)),
            //            MoveUp,
            //            Select(
            //                IfEqual(GetMaxNeigbour(), GetCellRange(GetManY, Inc(GetManX))),
            //                MoveRight,
            //                Select(
            //                    IfEqual(GetMaxNeigbour(), GetCellRange(Inc(GetManY), GetManX)),
            //                    MoveDown,
            //                    MoveRight
            //                )
            //            )
            //        )
            //    )
            //);

            return Return(
                MakePair(
                    Const(1),
                    Call(GetManXFunc, worldState)
                    )
            );

        }


        private object GetManXFunc(params object[] args)
        {
            object worldState = Param(args, 0);
            return Return(ElemInt(ElemList(ElemList(worldState, ManStatus), ManLocation), Const(0)));
        }

        public object GetCellRange(object y, object x)
        {
            //return Select(
            //    IfGreaterOrEqual(GetCell(y, x), ManStartPosition),
            //    EmptySpace,
            //    GetCell(y, x)
            //);
            return Add(Mul(IfGreaterOrEqual(GetCell(y, x), ManStartPosition), EmptySpace),
                              Mul(IfGreater(ManStartPosition, GetCell(y,x)), GetCell(y,x)));
        }

        public object GetMaxNeigbour()
        {
            return Max(
                GetCellRange(Inc(GetManY), GetManX), 
                GetCellRange(GetManY, Inc(GetManX)), 
                GetCellRange(Dec(GetManY), GetManX), 
                GetCellRange(GetManY, Dec(GetManX))
            );
        }

        private object GetMap()
        {
            const int worldState = 1;
            return ElemList(Param(_stepArgs, worldState), MapStatus);
        }
        
        private object GetMan()
        {
            const int worldState = 1;
            return ElemList(Param(_stepArgs, worldState), ManStatus);
        }

        private object GetManX
        {
            get {return ElemInt(ElemList(GetMan(), ManLocation), Const(0));}
        }

        private object GetManY
        {
            get { return ElemInt(ElemList(GetMan(), ManLocation), Const(1)); }
        }

        public object GetCell(object y, object x)
        {
            return ElemInt(ElemList(GetMap(), y), x);
        }

        private object Inc(object x)
        {
            return Add(x, Const(1));
        }

        private object Dec(object x)
        {
            return Sub(x, Const(1));
        }

        public object IfNotEqual(object a, object b)
        {
            return Sub(Const(1), IfEqual(a, b));
        }

        public object Not(object a)
        {
            return Sub(Const(1), a);
        }

        public object And(object a, object b)
        {
            return Mul(a, b);
        }

        public object Or(object a, object b)
        {
            return Not(And(Not(a), Not(b)));
        }

        public object Xor(object a, object b)
        {
            return IfNotEqual(a, b);
        }

        public object Max(object a, object b)
        {
            //return Select(IfGreater(a, b), a, b);
            return Add(Mul(a, IfGreater(a, b)), Mul(b, IfGreaterOrEqual(b, a)));
        }

        public object Max(object a, object b, object c)
        {
            return Max(Max(a, b), c);
        }

        public object Max(object a, object b, object c, object d)
        {
            return Max(Max(a, b, c), d);
        }

        public object Min(object a, object b)
        {
            return Select(IfGreater(b, a), a, b);
        }

        public object Min(object a, object b, object c)
        {
            return Min(Min(a, b), c);
        }

        public object Min(object a, object b, object c, object d)
        {
            return Min(Min(a, b, c), d);
        }

        //Константы

        private object AiState
        {
            get { return Const(0); }
        }

        private object WorldState
        {
            get { return Const(1); }
        }

        private object MapStatus
        {
            get { return Const(0); }
        }

        private object ManStatus
        {
            get { return Const(1); }
        }

        private object GhostStatus
        {
            get { return Const(2); }
        }

        private object FruitStatus
        {
            get { return Const(3); }
        }

        private object Wall
        {
            get { return Const(0); }
        }

        private object EmptySpace
        {
            get { return Const(1); }
        }

        private object Pill
        {
            get { return Const(2); }
        }

        private object PowerPill
        {
            get { return Const(3); }
        }

        private object Fruit
        {
            get { return Const(4); }
        }

        private object ManStartPosition
        {
            get { return Const(5); }
        }

        private object GhostStartPosition
        {
            get { return Const(6); }
        }

        private object MoveUp 
        { 
            get { return Const(0);             }
        }

        private object MoveRight
        {
            get { return Const(1); }
        }

        private object MoveDown
        {
            get { return Const(2); }
        }

        private object MoveLeft
        {
            get { return Const(3); }
        }

        private object ManVitality
        {
            get { return Const(0); }
        }

        private object ManLocation
        {
            get { return Const(1); }
        }

        private object ManDirection
        {
            get { return Const(2); }
        }

        private object ManLives
        {
            get { return Const(3); }
        }

        private object ManScore
        {
            get { return Const(4); }
        }

        public object MakeTurn(object[] args)
        {
            const int worldState = 2;
            //return Return(Select(
            //    IfEqual(Param(args, 0), Param(args, 1)),
            //    Add(Param(args, 2), Param(args, 3)),
            //    Sub(Param(args, 2), Param(args, 3))
            //    ));

            //return Return(
            //    Select(
            //        IfEqual(Param(args, 0), Param(args, 1)),
            //        Select(
            //            Const(1), 
            //            Const(2),
            //            Const(3)),
            //        Const(4)
            //    ));

            return Return(
                Select(
                    IfEqual(ElemInt(Param(args, 4), Const(2)), Param(args, 1)),
                    Select(
                        IfEqual(Const(2), Const(2)),
                        Add(Param(args, worldState), Param(args, 3)),
                        Sub(Param(args, worldState), Param(args, 3))),
                    Select(
                        IfEqual(Const(0), Const(0)),
                        Mul(Param(args, 2), Param(args, 3)),
                        Div(Param(args, 2), Param(args, 3)))
                ));

            //return Return(Select(
            //    IfEqual(Param(args, 0), Param(args, 1)),
            //    Add(Param(args, 2), Param(args, 3)),
            //    Param(args, 3)
            //    ));
            //return Add(Const(20), Sub(Param(args, 2), Div(Param(args, 0), Param(args, 1))));
        }

        public object Return(object a)
        {
            return f.Return(a);
        }

        public object Const(int a)
        {
            return f.Const(a);
        }

        public object Param(object[] paramsList, int number)
        {
            return f.Param(paramsList, number);
        }

        public object ElemInt(object list, object number)
        {
            return f.ElemInt(list, number);
        }

        public object ElemList(object list, object number)
        {
            return f.ElemList(list, number);
        }

        public object Add(object a, object b)
        {
            return f.Add(a, b);
        }

        public object Sub(object a, object b)
        {
            return f.Sub(a, b);
        }

        public object Mul(object a, object b)
        {
            return f.Mul(a, b);
        }

        public object Div(object a, object b)
        {
            return f.Div(a, b);
        }

        public object MakePair(object a, object b)
        {
            return f.MakePair(a, b);
        }

        public object MakeClosure(string name, Function func)
        {
            return f.MakeClosure(name, func);
        }

        public object Call(Function func, params object[] args)
        {
            return f.Call(func, args);
        }

        public object IfEqual(object a, object b)
        {
            return f.IfEqual(a, b);
        }

        public object IfGreater(object a, object b)
        {
            return f.IfGreater(a, b);
        }

        public object IfGreaterOrEqual(object a, object b)
        {
            return f.IfGreaterOrEqual(a, b);
        }

        public object Select(object cond, object ifTrue, object ifFalse)
        {
            return f.Select(cond, ifTrue, ifFalse);
        }

        public void Flush()
        {
            f.Flush();
        }
    }
}
